﻿// Copyright 2012 T4C: sejerpz@gmail.com
// Licensed under GPL v3

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;

namespace SET.Core
{
    class PlayMidi
    {
        private int handle = 0;

        // Handle midi messages sent from system.
        protected delegate void MidiCallback(int handle, int msg,
            int instance, int param1, int param2);

        [DllImport("winmm.dll")]
        private static extern int midiOutOpen(ref int handle, int deviceID,
            MidiCallback proc, int instance, int flags);

        [DllImport("winmm.dll")]
        private static extern int midiOutClose(int handle);

        [DllImport("winmm.dll")]
        protected static extern int midiOutShortMsg(int handle, int message);

        private static Dictionary<string, byte> noteNames;

        static PlayMidi()
        {
            noteNames = new Dictionary<string, byte>();
            noteNames.Add("C", 0);
            noteNames.Add("C#", 1);
            noteNames.Add("Db", 1);
            noteNames.Add("D", 2);
            noteNames.Add("D#", 3);
            noteNames.Add("Eb", 3);
            noteNames.Add("E", 4);
            noteNames.Add("Fb", 4);
            noteNames.Add("F", 5);
            noteNames.Add("F#", 6);
            noteNames.Add("Gb", 6);
            noteNames.Add("G", 7);
            noteNames.Add("G#", 8);
            noteNames.Add("Ab", 8);
            noteNames.Add("A", 9);
            noteNames.Add("A#", 10);
            noteNames.Add("Bb", 10);
            noteNames.Add("B", 11);
            noteNames.Add("Cb", 11);
        }

        public void PlayNote(int octave, string note)
        {
            PlayNote(octave, note, (int)Math.Round(Properties.Settings.Default.NoteOnTime * 1000), (int)Math.Round(Properties.Settings.Default.NoteOffTime * 1000));
        }

        public void PlayNote(int octave, string[] notes)
        {
            PlayNotes(octave, notes, (int)Math.Round(Properties.Settings.Default.NoteOnTime * 1000), (int)Math.Round(Properties.Settings.Default.NoteOffTime * 1000));
        }

        public void PlayNote(int octave, string note, int durationMs, int lastNoteReleaseTimeMs)
        {
            PlayNotes(octave, new string[] { note }, durationMs, lastNoteReleaseTimeMs);
        }

        public void PlayNotes(int octave, string[] notes, int durationMs, int lastNoteReleaseTimeMs)
        {
            if (octave < 0 || octave > 10)
                throw new ArgumentOutOfRangeException("octave");

            int attack = 0x40;
            int noteOn = 0x90;
            int noteOff = 0x80;

            int result = midiOutOpen(ref handle, 0, null, 0, 0);

            foreach (string note in notes)
            {
                int noteNo = (noteNames[note] + (octave * 12));

                // eg.  0x00403C90
                int val = (attack << 16) | (noteNo << 8) | noteOn;
                int r = midiOutShortMsg(handle, val);
                Thread.Sleep(durationMs);

                val = (attack << 16) | (noteNo << 8) | noteOff;
                midiOutShortMsg(handle, val);
            }

            Thread.Sleep(lastNoteReleaseTimeMs);
            midiOutClose(handle);
        }
    }
}